/*
 * Andy Hains
 * CS426 Project 2
 * Due: 11/22/2011
 */

/*
 * File:  buffer.c
 * --------------------
 * Code implementation of the buffer.
 */

#include "buffer.h"
#include <pthread.h>
#include <semaphore.h>

#ifndef BUFFER_C
#define BUFFER_C

pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
sem_t empty, full;

// The Buffer
buffer_item_t buffer[BUFFER_SIZE];

int buffer_init(void) {
/*
 * Function:  buffer_init
 * --------------------
 * Initialize the buffer.
 *
 * returns: 0 for success
 *          otherwise returns an error code
 */
    int ret = 0;
    ret += sem_init(&empty, 0, BUFFER_SIZE);
    ret += sem_init(&full, 0, 0);

    buffer_in = 0;
    buffer_out = 0;

    return ret;
}

int buffer_cleanup(void) {
/*
 * Function:  buffer_cleanup
 * --------------------
 * Destroys the semaphores and mutexes associated with the buffer.
 * Used when finished with the buffer.
 *
 * returns: 0 for success
 *          otherwise returns an error code
 */
    int ret = 0;

    int val;
    ret += sem_getvalue(&full, &val);
    printf("--Buffer had %d items remaining\n", val);

    ret += sem_destroy(&full);
    ret += sem_destroy(&empty);
    ret += pthread_mutex_destroy(&buffer_mutex);
    return ret;
}

int insert_item(buffer_item_t item) {
/*
 * Function:  insert_item
 * --------------------
 * Inserts an item into the buffer.
 * Synchronized with remove_item to avoid race conditions and to not go over the
 * bounds of the buffer.
 *
 * returns: 0 for success
 *         -1 indicates an error condition
 */
    int ret = 0;

    ret += sem_wait(&empty);
    ret += pthread_mutex_lock(&buffer_mutex);

    buffer[buffer_in] = item;
    buffer_in = (buffer_in+1) % BUFFER_SIZE;

    ret += pthread_mutex_unlock(&buffer_mutex);
    ret += sem_post(&full);

    if (ret != 0)
        return -1;
    else
        return 0;
}

int remove_item(buffer_item_t* item) {
/*
 * Function:  remove_item
 * --------------------
 * Removes an item from the buffer.
 * Synchronized with insert_item to avoid race conditions and to not go over the
 * bounds of the buffer.
 *
 * returns: 0 for success
 *         -1 indicates an error condition
 */
    int ret = 0;

    ret += sem_wait(&full);
    ret += pthread_mutex_lock(&buffer_mutex);

    *item = buffer[buffer_out];
    buffer_out = (buffer_out+1) % BUFFER_SIZE;

    ret += pthread_mutex_unlock(&buffer_mutex);
    ret += sem_post(&empty);

    if (ret != 0)
        return -1;
    else
        return 0;
}

#endif /* BUFFER_C */

